#include "include/port/swei_log.h"
#include "include/swei_cert.h"
#include "mmgr/include/mmgr_misc.h"
#include "global.h"
#include "task/comm/co_heartbeat.h"
#include "modem/qlmodem/task_modem.h"

/* 证书密钥上传步骤, 上传到 modem */
typedef enum{
    UPLOAD_IDLE,
    UPLOAD_WAIT_TIME_SYNC,

    UPLOAD_FILE_CHECK,

    UPLOAD_FILE,
    UPLOAD_FILE_WAIT,

    UPLOAD_END,
} upload_stage_e;

typedef struct{
    bool          is_mqtt_ssl;
    bool          is_https_up_done;
    bool          is_mqtt_up_done;

    uint8_t       upload_mask;
    uint8_t       done_mask;
    uint8_t       last_request_mask;
    uint8_t       file_stage;       /* 加载文件顺序步骤 */

    uint8_t       stage;            /* 加载TOKEN、证书、服务器信息等步骤 */
    bool          ack;              /* 是否上传成功 */
    uint32_t      timeout;          /* 计算上传超时 */
    uint8_t       nack_cnt;         /* 上传失败次数 */
    char          *crt_buffer;      /* 证书缓存 */
} mmgr_upload_cps_t;

static mmgr_upload_cps_t upload_cps = {
    .upload_mask        = 0,
    .done_mask          = 0,
    .last_request_mask  = 0,
    .file_stage         = 0,
    .stage              = UPLOAD_IDLE,
    .is_mqtt_ssl        = false,
    .ack                = false,
    .timeout            = 0,
    .nack_cnt           = 0,
    .crt_buffer         = NULL,
};






extern ssl_cfg_t ssl_cfg;
/* 最大重试次数 */
#define UPLOAD_NACK_MAX_CNT     3
#define UPLOAD_TIMEOUT          20000
/**
 * @brief SSL上传证书流程
 */

int32_t swei_cert_upload_process(void)
{
    int32_t ret = 0;

    switch(upload_cps.stage) 
    {
        case UPLOAD_IDLE:
            return -EPERM;
        break;

        case UPLOAD_WAIT_TIME_SYNC:
            if (1 /*rtc_sync_over_net()*/) {
                upload_cps.stage = UPLOAD_FILE_CHECK;
            } else if (timeout_ms(upload_cps.timeout, UPLOAD_TIMEOUT * 10)) {
                log_e( "Wait time sync fail.\r\n");
                ret = -ETIME;
                goto out;
            }
        break;

        case UPLOAD_FILE_CHECK: {
            uint8_t upload_mask = ((~upload_cps.upload_mask) | upload_cps.done_mask);
            for (int i = 0; i < 4; i++) {
                if (0 == (upload_mask & (0x01 << i))) {
                    upload_cps.file_stage = i;
                    upload_cps.stage = UPLOAD_FILE;
                    return -EBUSY;
                }
            }
            upload_cps.stage = UPLOAD_END;
        }
        break;

        case UPLOAD_FILE:
        {
            ssl_cert_t ssl_cert = {0};

            swei_socket_cert_get(&ssl_cert);
            ret = qltask_modem_file_upload(ssl_cfg.ca_cert, (const char *)ssl_cert.ca_ptr, strlen((char const *)ssl_cert.ca_ptr));
            // swei_file_upload(ssl_cfg.ca_cert, (uint8_t *)ssl_cert.ca_ptr,
            //                         strlen((char const *)ssl_cert.ca_ptr));
            if (0 != ret)
            {
                log_e("Upload <%s> file failed! ret=%d\r\n", ssl_cfg.ca_cert, ret);
            }
            else
            {
                log_d("Upload <%s> file...  size=%d\r\n", ssl_cfg.ca_cert, strlen(ssl_cert.ca_ptr));
            }
            upload_cps.timeout  = getms();
            upload_cps.ack      = false;
            upload_cps.nack_cnt = 0;
            upload_cps.stage    = UPLOAD_FILE_WAIT;
        }
        break;

        case UPLOAD_FILE_WAIT:
        {
            ssl_cert_t ssl_cert = {0};

            /* 超时处理 */
            if (!upload_cps.ack && timeout_ms(upload_cps.timeout, UPLOAD_TIMEOUT)) {
                if (upload_cps.nack_cnt++ >= UPLOAD_NACK_MAX_CNT) {
                    ret = -EPERM;
                    goto out;
                }

                swei_socket_cert_get(&ssl_cert);
                // 超时后立即触发一次重发？
                ret = qltask_modem_file_upload(ssl_cfg.ca_cert, (const char *)ssl_cert.ca_ptr, strlen((char const *)ssl_cert.ca_ptr));
                // ret = swei_file_upload(ssl_cfg.ca_cert, (uint8_t *)ssl_cert.ca_ptr,
                //                     strlen((char const *)ssl_cert.ca_ptr));
                if(0 != ret) {
                    log_e("Upload <%s> file failed! ret=%d\r\n", ssl_cfg.ca_cert, ret);
                } else {
                    log_d("Upload <%s> file...  size=%d\r\n", ssl_cfg.ca_cert, strlen(ssl_cert.ca_ptr));
                }
                upload_cps.timeout  = getms();
                upload_cps.ack      = false;
            } else if (upload_cps.ack) {        /* 上传成功，执行下一步 */
                upload_cps.stage    = UPLOAD_FILE_CHECK;   
            }
        }
        break;

        case UPLOAD_END:                    /* 上传成功 */
            log_d( "Modem file upload complete.\r\n");
            ret = 0;
            goto out;
        break;
    }

    return -EBUSY;                      /* 正在上传 */

out:
    if (NULL != upload_cps.crt_buffer) {
        qlfree(upload_cps.crt_buffer);
        upload_cps.crt_buffer = NULL;
    }
    upload_cps.stage = UPLOAD_IDLE;
    upload_cps.upload_mask = 0;

    if (0 != ret)
        log_d( "Modem file upload fail, rease is: 0x%02X.\r\n", ret);

    return ret;                           /* 上传成功 */
}



/*************************************************************************
 * 函数名称：swei_cert_status_update
 * 函数功能：modem 文件上传状态更新事件
 * 输入参数：prm-0成功，负数为错误码，data-上传文件名，size-文件名长度
 * 返 回 值：void
 *************************************************************************/
void swei_cert_status_update(int32_t prm, void *data, size_t size)
{
    if(0 == prm && UPLOAD_FILE_WAIT == upload_cps.stage) {          /* 文件传输成功 */
        upload_cps.ack = true;
        upload_cps.done_mask |= MMGR_UPLOAD_TLS_CA;
        upload_cps.upload_mask &= ~MMGR_UPLOAD_TLS_CA;
    }
}


/* 初始化证书上传流程, 随后重新运行上传流程 */
int32_t swei_cert_upload_request(uint8_t upload_mask)
{
    uint8_t start_mask = 0;

    start_mask = (~upload_mask) | upload_cps.done_mask;
    if (0xFF == start_mask) {
        return 0;
    }

    /* 避免频繁请求 */
    if (upload_cps.last_request_mask == upload_mask && !timeout_ms(upload_cps.timeout, UPLOAD_TIMEOUT))
        return -EPERM;

    upload_cps.last_request_mask = upload_mask;
    upload_cps.upload_mask |= upload_mask;
    if (UPLOAD_IDLE != upload_cps.stage)
        return -EBUSY;

    upload_cps.stage    = UPLOAD_WAIT_TIME_SYNC;
    upload_cps.timeout  = getms();
    upload_cps.ack      = false;
    upload_cps.nack_cnt = 0;
    upload_cps.crt_buffer = NULL;

    log_d( "Requst upload file mask is: 0x%02X, upload completion is 0x%02X.\r\n", 
        upload_mask, upload_cps.done_mask);

    return -EBUSY;
}